package server;
import common.*;
import common.CMessage.*;

public class CServer extends Thread{
	public enum ServState{WAITING_FOR_PLAYERS, ALL_PLAYERS_CONNECTED};
	protected int i_totplayer;
	private int i_connectedplayercount;
	protected CClientS[] arr_clientS; public CClientS[] GetClients(){return arr_clientS;}
	private CMultiServer o_multiserver;
	private ServState e_servstate; public ServState GetServerState(){return e_servstate;} 
	protected CProcessMsgThread t_processmsg;
	protected boolean b_running;
	
	public void SetServerState(ServState state){
		e_servstate=state;
//		if(state==ServState.ALL_PLAYERS_CONNECTED){
//			synchronized(syncobject){
//				syncobject.notify();
//			}
//		}
	}
	
	private CMessage GenConnectedClientInfo(int ownid){
		CMessage msg = new CMessage(MsgType.SERVMSG);
		msg.i_clientid = ownid;
		msg.e_datatype = DataType.CONNECTED_CLIENT_INFO;
		msg.arr_data = new CClientInfo[i_totplayer];
		for(int i =0; i<i_totplayer;i++){
			CClientInfo clientinfo = new CClientInfo();
			msg.arr_data[i] = clientinfo;
			clientinfo.i_clientid = arr_clientS[i].Getid();
			clientinfo.s_clientname = arr_clientS[i].GetName();
			clientinfo.s_clientip = arr_clientS[i].GetIP();
		}
		msg.l_msgid = arr_clientS[ownid].i_outmsg_cnt++;
		return msg;
	}
	
	public void NewClientConnected(int clientid){
		
		this.i_connectedplayercount++;
		for(int i=0; i<this.i_connectedplayercount; i++)
			this.arr_clientS[i].q_msg_out.add(GenConnectedClientInfo(i));
		
		System.out.println("New Client Connected, clientid " + clientid);
		if(i_connectedplayercount == i_totplayer)
			SetServerState(ServState.ALL_PLAYERS_CONNECTED);
	}
	private int i_portnumber; public int GetPort(){return i_portnumber;}
	public CServer(int port, int totplayer, boolean isChild){
		i_portnumber = port;
		i_totplayer = totplayer;
		this.i_connectedplayercount = 0;
		o_multiserver = new CMultiServer(this);
		e_servstate = ServState.WAITING_FOR_PLAYERS;

		if(!isChild){
			arr_clientS = new CClientS[i_totplayer];
			for(int i=0; i<i_totplayer; i++)
				arr_clientS[i] = new CClientS();

		}
		this.b_running = false;
	}
	
	public int GetNextAvailableClientId(){
		for(int i = 0; i < i_totplayer; i++){
			if(arr_clientS[i].Getid()==-1)
				return i;
		}
		return -1;
	}
	public void Init() throws InterruptedException{
		//Step 1. Create ClientData Structure and wait until all clients connect
		o_multiserver.start();
		this.t_processmsg = new CProcessMsgThread(this);
		this.t_processmsg.start();
//		System.out.println("Server Started: Now waiting for clients to connect");
//		synchronized(syncobject){
//			while(e_servstate != ServState.ALL_PLAYERS_CONNECTED){
//				System.out.println("Till now there are only " + this.i_connectedplayercount + " connected players.");
//				syncobject.wait(10000);
//			}
//		}
//		System.out.println("All Players Connected");
		this.b_running = true;
		return;
	}

	public void abort(){
		b_running = false;
		synchronized(this){
			this.notifyAll();
		}
		t_processmsg.abort();
		o_multiserver.abort();
	}
	protected void ProcessMsg(Object obj){
		CMessage msg = (CMessage)obj;
		if(msg.e_type!=MsgType.CLIENTMSG){
			System.err.println("Not valid Message Type ");
			return;
		}
		switch(msg.e_datatype){
		case MSG_CHAT:
			String sChat = (String)msg.arr_data[0];
			CMessage send = CMessage.CreateMsg_Chat_Serv(-1, -1, arr_clientS[msg.i_clientid].GetName() + ": " + sChat);
			BroadCast(send);
			break;
		default:
			System.err.println("CServer::ProcessMsg::Unknown Msg Type");
		}
		return;
	}
	protected void BroadCast(CMessage msg){
		
		for(int i=0;i<this.i_totplayer;i++){
			msg.i_clientid = this.GetClients()[i].Getid();
			if(msg.i_clientid!=-1){
				msg.l_msgid = this.GetClients()[i].i_outmsg_cnt++;
				this.GetClients()[i].q_msg_out.add(msg);
			}
		}
	}
	protected void ProcessNextMsg(){
		for(int i=0; i<this.i_totplayer;i++){
			if((arr_clientS[i].Getid()!=-1)&&(!this.arr_clientS[i].q_msg_in.isEmpty())){
				this.arr_clientS[i].i_inmsg_cnt++;
				ProcessMsg(this.arr_clientS[i].q_msg_in.poll());
			}
		}
	}
	
	public void run(){
		try {
			Init();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	protected class CProcessMsgThread extends Thread{
		private CServer o_server;
		private boolean running;
		public CProcessMsgThread( CServer o_server){
			this.o_server = o_server;
			running = false;
		}
		public void abort(){
			running = false;
			synchronized(o_server){
				o_server.notifyAll();
			}
		}
		public void run(){
			running = true;
			while(running){				
				try {
					synchronized(o_server){
						o_server.ProcessNextMsg();
						o_server.wait();
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	 
}
